import numpy as np
import json

def compute_semitones(frequencies, ref_freq=None):
    """计算半音值
    
    Args:
        frequencies (list): 基频列表（Hz）
        ref_freq (float, optional): 参考频率。如果不提供，取列表最小值
        
    Returns:
        list: 半音值列表
    """
    if ref_freq is None:
        # 获取有效的最小值作为参考频率
        valid_freq = [f for f in frequencies if f > 0]
        if not valid_freq:
            return [0] * len(frequencies)
        ref_freq = min(valid_freq)
    
    # 应用半音公式: 12 * log2(f/f_ref)
    semitones = [12 * np.log2(f / ref_freq) if f > 0 else 0 for f in frequencies]
    return semitones, ref_freq

def normalize_time(time_points):
    """将时间点归一化到0-1范围
    
    Args:
        time_points (list): 时间点列表
        
    Returns:
        list: 归一化后的时间点列表
    """
    if not time_points:
        return []
        
    min_time = min(time_points)
    max_time = max(time_points)
    
    # 避免除以零
    if max_time == min_time:
        return [0.5] * len(time_points)
    
    return [(t - min_time) / (max_time - min_time) for t in time_points]

def fit_curve(normalized_time, semitones, tone_type):
    """根据声调类型拟合曲线
    
    Args:
        normalized_time (list): 归一化后的时间点
        semitones (list): 半音值
        tone_type (str): 声调类型
    
    Returns:
        tuple: (x_fit, y_fit, coeffs, poly) 拟合后的曲线点和参数
    """
    # 创建更密集的点以获得平滑曲线
    x_fit = np.linspace(0, 1, 100)
    
    # 根据声调类型选择拟合度数
    if "降升调" in tone_type or "起伏调" in tone_type or "三声" in tone_type or tone_type == 3:
        # 使用二次多项式拟合
        degree = 2
    else:
        # 其他声调使用一次多项式拟合
        degree = 1
    
    # 拟合多项式
    coeffs = np.polyfit(normalized_time, semitones, degree)
    poly = np.poly1d(coeffs)
    y_fit = poly(x_fit)
    
    return x_fit, y_fit, coeffs, poly

def get_fit_type_from_tone(tone_type):
    """根据声调类型获取拟合类型
    
    Args:
        tone_type: 声调类型，可以是字符串或数字
        
    Returns:
        int: 拟合类型，1表示一次函数，2表示二次函数
    """
    if isinstance(tone_type, str):
        if "降升调" in tone_type or "起伏调" in tone_type or "三声" in tone_type:
            return 2
        return 1
    elif isinstance(tone_type, (int, float)):
        # 如果是数字，那么3代表三声，需要二次函数拟合
        return 2 if tone_type == 3 else 1
    else:
        # 默认使用一次函数拟合
        return 1

def extract_fit_coefficients(coeffs):
    """从拟合系数中提取a, b, c参数
    
    Args:
        coeffs: 拟合系数数组
        
    Returns:
        dict: 拟合参数，一次函数有a,b，二次函数有a,b,c
    """
    result = {}
    if len(coeffs) == 2:  # 一次函数: y = ax + b
        result = {
            'a': float(coeffs[0]),
            'b': float(coeffs[1])
        }
    elif len(coeffs) == 3:  # 二次函数: y = cx^2 + ax + b
        result = {
            'c': float(coeffs[0]),
            'a': float(coeffs[1]),
            'b': float(coeffs[2])
        }
    return result

def analyze_audio_data(data_json):
    """分析音频数据，返回归一化点和拟合参数
    
    Args:
        data_json: 包含频率和时间数据的JSON对象
        
    Returns:
        dict: 包含归一化点、拟合参数和曲线点的结果
    """
    try:
        # 解析JSON数据
        if isinstance(data_json, str):
            data = json.loads(data_json)
        else:
            data = data_json
        
        # 验证数据格式
        if not isinstance(data, dict) or "data" not in data:
            return {"error": "数据格式错误: 缺少'data'字段"}
            
        if not data["data"] or len(data["data"]) == 0:
            return {"error": "数据为空"}
            
        # 提取频率和时间数据
        try:
            frequencies = [float(point.get("frequency", 0)) for point in data["data"]]
            time_points = [float(point.get("time", 0)) for point in data["data"]]
        except (ValueError, TypeError, AttributeError) as e:
            return {"error": f"数据解析错误: {e}"}
        
        # 过滤出有效的频率值
        valid_indices = [i for i, f in enumerate(frequencies) if f > 0]
        
        if not valid_indices:
            return {"error": "没有检测到有效的频率数据"}
            
        valid_frequencies = [frequencies[i] for i in valid_indices]
        valid_time_points = [time_points[i] for i in valid_indices]
        
        # 检查数据点是否足够
        if len(valid_frequencies) < 2:
            return {"error": "有效数据点不足，至少需要2个有效的频率值"}
        
        # 归一化处理
        normalized_time = normalize_time(valid_time_points)
        
        # 计算半音值
        semitones, ref_freq = compute_semitones(valid_frequencies)
        
        # 判断声调类型
        tone_type = ""
        semitone_range = max(semitones) - min(semitones)
        
        if semitone_range < 3:
            tone_type = "高平调"
        elif semitone_range >= 3 and semitone_range < 6:
            if semitones[-1] > semitones[0]:
                tone_type = "升调"
            else:
                tone_type = "降调"
        else:
            # 根据数据长度调整分段分析
            if len(semitones) >= 9:  # 足够长才分析复杂声调
                segments_count = min(3, len(semitones) // 3)
                first_segment = semitones[:segments_count]
                mid_segment = semitones[segments_count:2*segments_count]
                last_segment = semitones[-segments_count:]
                
                if np.mean(first_segment) > np.mean(mid_segment) and np.mean(last_segment) > np.mean(mid_segment):
                    tone_type = "降升调"
                elif semitones[0] > semitones[-1]:
                    tone_type = "全降调"
                elif semitones[-1] > semitones[0]:
                    tone_type = "全升调"
                else:
                    tone_type = "起伏调"
            else:  # 数据点较少时采用简化分析
                if semitones[-1] > semitones[0]:
                    tone_type = "升调"
                elif semitones[0] > semitones[-1]:
                    tone_type = "降调"
                else:
                    tone_type = "平调"
        
        # 获取拟合类型
        fit_type = get_fit_type_from_tone(tone_type)
        
        # 拟合曲线
        x_fit, y_fit, coeffs, poly = fit_curve(normalized_time, semitones, tone_type)
        
        # 提取拟合参数
        fit_params = extract_fit_coefficients(coeffs)
        fit_params['fit_type'] = fit_type
        
        # 构建返回结果
        normalized_points = [
            {"x": x, "y": y} for x, y in zip(normalized_time, semitones)
        ]
        
        fit_curve_points = [
            {"x": float(x), "y": float(y)} for x, y in zip(x_fit, y_fit)
        ]
        
        return {
            "status": "success",
            "normalized_points": normalized_points,
            "fit_params": fit_params,
            "fit_curve_points": fit_curve_points,
            "tone_type": tone_type,
            "stats": {
                "mean": float(np.mean(semitones)),
                "min": float(np.min(semitones)),
                "max": float(np.max(semitones)),
                "range": float(semitone_range)
            }
        }
    except Exception as e:
        return {"status": "error", "message": str(e)} 